#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <sys/un.h>
#include <time.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#include <sys/param.h>
#include <sys/socket.h>
#include <net/if.h>
#include <netinet/in.h>
#include <net/if_arp.h>
#include <string.h>
#include <strings.h>
#include <netinet/ip.h>
#include <netinet/ip_icmp.h>
#include <netinet/in.h>
#include <netinet/in_systm.h>
#include <netdb.h>
#include <setjmp.h>
#include <signal.h>
#include <errno.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <sys/types.h>
#include <mysql/mysql.h>
#include <pthread.h>

#define IP "10.0.0.1"
#define ping_time 100000
#define BUFSIZE 1024

void init_time(void);
void init_sigaction(void);
void handle(int);

struct QueryData
{
    MYSQL_ROW rowData;
    int colData;
    int rownum;
};

struct proto{
    void (*fproc)(char *,ssize_t,struct msghdr *,struct timeval *,int j);
    void (*fsend)(int j);
    void (*finit)(void);
    struct sockaddr *sasend;
    struct sockaddr *sarecv;
    socklen_t salen;
    int icmpproto;
}*pr;

char buf[256],tmp[256];
char nat_ip[64],host[64];
char mysql_pwd[64];
double sleeptime=ping_time;
int datalen=56;
int ipflag=1;
pid_t pid[256],recvpid[256];
int nsend[256],nreceived[256];
double temp_rtt[256][10000];
double all_time[256];
int sendId[256];
int pppfd[256];

int changeData(char *data_Name,char *handleData);
struct QueryData queryData(char *data_Name,char *handleData );
void tv_sub(struct timeval *out,struct timeval *in);
void proc_v4(char *ptr,ssize_t len,struct msghdr *msg,struct timeval * tvrecv,int j);
uint16_t in_cksum(uint16_t *addr,int len);
void send_v4(int j);
int readloop(int j);
struct addrinfo *host_serv(const char *host,const char *serv,int family,int socktype);
void handle_quit(int signo);
void * ppppingmer(void* args);
void * recvppp(void* args);



struct proto proto_v4={proc_v4,send_v4,NULL,NULL,NULL,0,IPPROTO_ICMP};



int main(int argc,char *argv[])
{
	int i,j,result;
	pthread_t ppprecv[256],pppping[256];
	struct sockaddr_in localppp[256];
	
	memset(mysql_pwd,0,sizeof(mysql_pwd));
	memset(host,0,sizeof(host));
	memset(tmp,0,sizeof(tmp));
	memset(buf,0,sizeof(buf));
	memset(nat_ip,0,sizeof(nat_ip)); 

	strcpy(host,IP);
	strcpy(mysql_pwd,"bjtungirc");
	struct QueryData query;
	query=queryData("dyninfo","select nat_ip from init_dyna;");
	strcpy(nat_ip,query.rowData[0]);

//***************************************************************************************	
	opterr=0;
	while((result=getopt(argc,argv,"h:t:s:"))!=-1)
    {
        switch(result)
        {
            case 'h':
                strcpy(host,optarg);
                break;
			case 't':
				sleeptime=atof(optarg);
				break;
				
			case 's':
				strcpy(nat_ip,optarg);
				break;
            default:
                printf("Unknown option\n");
              
        }
    }//end of while
//****************************************************************************************

	

	printf("\n\n\n\n\n");
	printf("                           **********              host=%s                    *************\n",host);
	printf("                           **********              sleeptime=%f               *************\n",sleeptime/1000000);
    sprintf(tmp,"%s",nat_ip);
	printf("                           **********              sourceIP=%s           *************\n",tmp);
	printf("\n\n\n\n\n");
//*******************************************************************************************
    struct addrinfo *ai;
    char h[20]={0};
    ai=host_serv(host,NULL,0,0);
    inet_ntop(AF_INET,&((struct sockaddr_in*)(ai->ai_addr))->sin_addr,h,sizeof(h));
    printf("PING %s (%s):%d data bytes\n",
            ai->ai_canonname?ai->ai_canonname:h,h,datalen);

    if(ai->ai_family==AF_INET){
        pr=&proto_v4;
    }else{
        printf("unknown address family %d\n",ai->ai_family);
    }

    pr->sasend=ai->ai_addr;
    pr->sarecv=(struct sockaddr*)calloc(1,ai->ai_addrlen);
    pr->salen=ai->ai_addrlen;
//*******************************************************************************************
//	for(i=64;i<255;i+=64)
//	{
	pppfd[0]=socket(pr->sasend->sa_family,SOCK_RAW,pr->icmpproto);
    setsockopt(pppfd[0],IPPROTO_IP,IP_HDRINCL,(char *)&ipflag,sizeof(int));
/*		if(1)
		{
			bzero(&localppp[i],sizeof(struct sockaddr_in));
       		localppp[i].sin_family=AF_INET;
         	localppp[i].sin_addr.s_addr=htonl(INADDR_ANY);
     		bind(pppfd[i],(const struct sockaddr *)&localppp[i],sizeof(struct sockaddr_in));
		}
*/
	//	pthread_create(&ppprecv[i],NULL,recvppp,(void*)i);
	pthread_create(&pppping[0],NULL,ppppingmer,(void*)0);
//		sleep(1);
//	}
	//init_sigaction();
	//init_time();
	while(1)
		sleep(10);

	return 0;
}//end of main



int changeData(char *data_Name,char *handleData)
{
    MYSQL mysql,*sock;
    MYSQL_RES *res;
    MYSQL_FIELD *my_fd;
    MYSQL_ROW row;
    mysql_init(&mysql);
    if(!(sock=mysql_real_connect(&mysql,"localhost","root",mysql_pwd,data_Name,0,NULL,0)))
    {
        printf("\nCouldn't connect to engine! Error:%s\n",mysql_error(&mysql));
        return -1;
    }
    if(mysql_query(sock,handleData))
    {
        printf("\nHandle failed! Reason:%s\n", mysql_error(sock));
        return -2;
    }
    else
    {
        printf("\nHandle Successfully!\n");
        return 0;
    }
    mysql_close(sock);
}



struct QueryData queryData(char *data_Name,char *handleData )
{
    MYSQL mysql,*sock;
    MYSQL_RES *res;
    MYSQL_FIELD *my_fd;
    MYSQL_ROW row;
    struct QueryData lastrow;
    mysql_init(&mysql);
    lastrow.rowData = NULL;
    lastrow.colData = 0;
    if(!(sock=mysql_real_connect(&mysql,"localhost","root",mysql_pwd,data_Name,0,NULL,0)))
    {
        printf("\nCouldn't connect to engine! Error:%s\n",mysql_error(&mysql));
        return lastrow;
    }
    if(mysql_query(sock,handleData))
    {
        printf("\nHandle failed! Reason:%s\n", mysql_error(sock));
        return lastrow;
    }
    if(!(res=mysql_store_result(sock)))
    {
        printf("Coun't get result from %s\n", mysql_error(sock));
        return lastrow;
    }
    lastrow.colData = mysql_num_fields(res);
    lastrow.rownum = mysql_num_rows(res);
    row = mysql_fetch_row(res);
    mysql_free_result(res);
    mysql_close(sock);
    lastrow.rowData = row;
    return lastrow;
}



void tv_sub(struct timeval *out,struct timeval *in)
{
    if((out->tv_usec-=in->tv_usec)<0)
    {
        --out->tv_sec;
        out->tv_usec+=1000000;
    }
    out->tv_sec-=in->tv_sec;
}



void proc_v4(char *ptr,ssize_t len,struct msghdr *msg,struct timeval * tvrecv,int j)
{
    int hlen1,icmplen;
    char host[20];
    double rtt;
    struct ip *ip;
    struct icmp *icmp;
    struct timeval *tvsend;
    ip=(struct ip*)ptr;
    hlen1=ip->ip_hl<<2;
    if(ip->ip_p!=IPPROTO_ICMP)
    {
        return;
    }
    icmp=(struct icmp*)(ptr+hlen1);
    if((icmplen=len-hlen1)<8)
        return;
    if(icmp->icmp_type==ICMP_ECHOREPLY)
    {
        if(icmp->icmp_id!=pid[j])
            return;
        if(icmplen<16)
            return;
        tvsend=(struct timeval*)icmp->icmp_data;
        tv_sub(tvrecv,tvsend);
        rtt=tvrecv->tv_sec*1000.0+tvrecv->tv_usec/1000.0;
        if(rtt>5000)
            return;
        nreceived[j]++;
        temp_rtt[j][nreceived[j]] = rtt;
        all_time[j] += rtt;
        printf("By ppp%d %d bytes from %s:seq=%u,ttl=%d,rtt=%.3f ms\n",j,icmplen,inet_ntop(AF_INET,&((struct sockaddr_in*)(pr->sasend))->sin_addr,host,sizeof(host)),icmp->icmp_seq,ip->ip_ttl,rtt);
    }
}




uint16_t in_cksum(uint16_t *addr,int len)
{
    int nleft=len;
    uint32_t sum=0;
    uint16_t *w=addr;
    uint16_t answer=0;

    while(nleft>1){
        sum+=*w++;
        nleft-=2;
    }
    if(nleft==1){
        *(unsigned char *)(&answer)=*(unsigned char *)w;
        sum+=answer;
    }
    sum=(sum >> 16)+(sum & 0xffff);
    sum+=(sum>>16);
    answer=~sum;
    return answer;
}




void send_v4(int j)
{
    int len;
    char str[128];
    struct icmp *icmp;

    struct iphdr * ip_header;
    char *buffer;
    unsigned short buffer_size;
    buffer_size=sizeof(struct iphdr)+ sizeof(struct icmp)+ sizeof(struct timeval);
    buffer=(char *)malloc(buffer_size);
    memset(buffer, 0, sizeof(buffer));
    ip_header=(struct iphdr *)buffer;
    ip_header->ihl=5;
    ip_header->version=4;
    ip_header->tos=((j+1)<<2);
    ip_header->tot_len=htons(buffer_size);
    ip_header->id=rand();
    ip_header->ttl=64;
    ip_header->frag_off=0x40;
    ip_header->protocol=IPPROTO_ICMP;
    ip_header->check=0;
    ip_header->daddr=inet_addr(host);
    sprintf(tmp,"%s",nat_ip);
    ip_header->saddr=inet_addr(tmp);
    //	ip_header->daddr=inet_addr(host);
    //	ip_header->saddr=inet_addr(inet_ntoa(((struct sockaddr_in*)(&storeip[j].ifr_addr))->sin_addr));
   	// printf("the source addr==============%s\n",inet_ntoa(((struct sockaddr_in*)(&storeip[j].ifr_addr))->sin_addr));
    icmp=(struct icmp*)(ip_header+1);
    icmp->icmp_type=ICMP_ECHO;  //type
    icmp->icmp_code=0;          //code
    icmp->icmp_id=pid[j];
    icmp->icmp_seq=sendId[j]++;
    nsend[j]++;
    gettimeofday((struct timeval *)icmp->icmp_data,NULL);//get send time
    len=8+datalen;
    buffer_size=sizeof(struct iphdr)+ len;
    icmp->icmp_cksum=0;
    icmp->icmp_cksum=in_cksum((u_short *)icmp,len);
    int testp;
//	printf("sendhhhhhhhhhhhhhhhhhhhh\n");
    testp=sendto(pppfd[j],buffer,buffer_size,0,pr->sasend,pr->salen);//send data
    free(buffer);
}





int readloop(int j)
{
    int i=0;
    int size;
    char recvbuf[BUFSIZE];  //get the response
    char controlbuf[BUFSIZE];
    struct msghdr msg;
    struct iovec iov;       //send data
    ssize_t n;
    struct timeval tval;

    //pppfd[j]=socket(pr->sasend->sa_family,SOCK_RAW,pr->icmpproto);

    //set effective uid to real uid
    setuid(getuid());
    //³õÊ¼»¯
    if(pr->finit)
        (*pr->finit)();

    size=60*1024;
    //ÉèÖÃÊÕ°ü»º´æ´óÐ¡
    setsockopt(pppfd[j],SOL_SOCKET,SO_RCVBUF,&size,sizeof(size));//set recvbuf size
    //init buffer
    iov.iov_base=recvbuf;   //set recvbuf
    iov.iov_len=sizeof(recvbuf);//recvbuf len
    msg.msg_name=pr->sarecv;//sockaddr
    msg.msg_iov=&iov;
    msg.msg_iovlen=1;
    msg.msg_control=controlbuf;

    //loop ping
    for(;;)
	{
        msg.msg_namelen=pr->salen;
        msg.msg_controllen=sizeof(controlbuf);
        n=recvmsg(pppfd[j],&msg,0);   //receive data
        if(n<0)

		{
            //printf("recvmsg error\n");
            perror("recvmsg error:");
            if(errno=EINTR)
                continue;
            else
                printf("recvmsg error\n");
        }
        gettimeofday(&tval,NULL);//receive time
        (*pr->fproc)(recvbuf,n,&msg,&tval,j);//handle receive data
    }
}





struct addrinfo *host_serv(const char *host,const char *serv,int family,int socktype)
{
    int n;
    struct addrinfo hints,*res;

    bzero(&hints,sizeof(hints));
    hints.ai_flags=AI_CANONNAME;
    hints.ai_family=family;
    hints.ai_socktype=socktype;
    if((n=getaddrinfo(host,serv,&hints,&res))!=0)
	{
        return NULL;
    }
    return (res);
}

void handle_quit(int signo)
{
    pthread_exit(NULL);
}




void * ppppingmer(void* args)
{
    int thread_arg;
    thread_arg = (int)args;
    pid[thread_arg]=(long int)syscall(224);
    for(;;)
    {
        (*pr->fsend)(thread_arg);
        usleep(sleeptime);
    }
}


/*
*ÊÕ°ü
*/
void * recvppp(void* args)
{
    int thread_arg;
    thread_arg = (int)args;
    recvpid[thread_arg] = (long int)syscall(224);
    if((readloop(thread_arg))==0)
    {
        return;
    }
}
void init_time(void)
{
    //¶šÒå¶šÊ±Œäžô
    struct itimerval val;
    //ÃëÎª1S
    val.it_value.tv_sec=30;
    //ºÁÃëÎª0
    val.it_value.tv_usec=0;//Éè¶š¶šÊ±ŒäžôÎª0.5s
    //¶šÊ±ºÍŒäžôÏàÍ¬
    val.it_interval=val.it_value;
    //Éè¶š¶šÊ±
    setitimer(ITIMER_REAL,&val,NULL);
};

void init_sigaction(void)
{
    //¶šÒå¶¯×÷
    struct sigaction act;
    act.sa_handler=handle;
    act.sa_flags=0;
    sigemptyset(&act.sa_mask);
    sigaction(SIGALRM,&act,NULL);
};
void handle(int signo)
{
	exit(1);
}
